---
id: Features_CLI_args-validation
title: Argument Parsing and Validation in FBOSS2 CLI
---

# Argument Parsing and Validation in FBOSS2 CLI

## Description

In this article, we will look at how argument parsing of subcommand works and how to implement argument validation for FBOSS2 CLI.

## Add a New Type of Argument

The below shows a step-by-step instruction on how to add a new type of argument.

1. Define a new type of argument id in [CmdCommonUtils.h::ObjectArgTypeId](https://www.internalfb.com/code/fbsource/[e5984a8d19eebbaac834e47b5372e9a6a39bd41b]/fbcode/fboss/cli/fboss2/utils/CmdCommonUtils.h?lines=33).

2. Subclass [BaseObjectArgType](https://www.internalfb.com/code/fbsource/[e5984a8d19eebbaac834e47b5372e9a6a39bd41b]/fbcode/fboss/cli/fboss2/utils/CmdCommonUtils.h?lines=60) with the argument id defined in step 1 and implement validation rules in the class constructor.

3. Add unit test for the newly created argument type in `CmdArgsTest.cpp` (For example [D37385960](https://www.internalfb.com/diff/D37385960)/[D37329534](https://www.internalfb.com/diff/D37329534)).

4. Register your command in `fboss/cli/fboss2/CmdList.cpp`.

5. Use the newly created type in your command file (For example [D37411284](https://www.internalfb.com/diff/D37411284)). Be sure to specify ObjectArgTypeId in the command traits class with the new argument type.
Although the `BaseObjectArgType` has implemented interface including `empty()`, `size()`, iterator and indexing, you could also access the original data and utilize the feature of `std::vector` by calling `.data()`.
For old type migration, you will need to modify `ObjectArgType` under `CmdXXXTraits` and adjust the argument type in `queryClient`.

6. Add a subcommand option to process the new argument type in [CmdSubcommands.cpp](https://www.internalfb.com/code/fbsource/[29256fef57b039281881805f7698decb12d688a3]/fbcode/fboss/cli/fboss2/CmdSubcommands.cpp?lines=49).


## How Subcommand Arg Parsing Works

In this section, we will use the command`fboss2 set interface prbs state [OPTIONS] [state…]` as an example

`CommandTree& kCommandTree()` in file `fboss/cli/fboss2/CmdList.cpp` could demonstrate the hierarchical relationship between the three arguments.

```
{
  "set",
  "interface",
  "Set Interface information",
  commandHandler<CmdSetInterface>,
  argTypeHandler<CmdSetInterfaceTraits>,
  {{
    "prbs",
    "Set PRBS properties",
    commandHandler<CmdSetInterfacePrbs>,
    argTypeHandler<CmdSetInterfacePrbsTraits>,
    {{"state",
      "Set PRBS state",
      commandHandler<CmdSetInterfacePrbsState>,
      argTypeHandler<CmdSetInterfacePrbsStateTraits>}},
  }},
}
```
However, `kCommandTree()` is a runtime concept that deals with user input from command line. The actual type of each argument is determined in the `CmdXXXTrait` during compilation time.

`ObjectArgType` defines the expected type of argument (Notice that they used to be `std::vector<std::string>` before we implement the feature argument validation).

By specifying `ParentCmd`, the subcommands are able to inherit the argument from their parent commands and construct a typed array of arguments (will be covered below) during compilation time.

```
// fboss/cli/fboss2/commands/set/interface/CmdSetInterface.h
struct CmdSetInterfaceTraits : public BaseCommandTraits {
  static constexpr utils::ObjectArgTypeId ObjectArgTypeId =
      utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_PORT_LIST;
  using ObjectArgType = utils::PortList; // used to be std::vector<std::string>;
  using RetType = std::string;
};
// fboss/cli/fboss2/commands/set/interface/prbs/CmdSetInterfacePrbs.h
struct CmdSetInterfacePrbsTraits : public BaseCommandTraits {
  static constexpr utils::ObjectArgTypeId ObjectArgTypeId =
      utils::ObjectArgTypeId::OBJECT_ARG_TYPE_PRBS_COMPONENT;
  using ParentCmd = CmdSetInterface;
  using ObjectArgType = utils::PrbsComponent; // used to be std::vector<std::string>
  using RetType = std::string;
};
// fboss/cli/fboss2/commands/set/interface/prbs/CmdSetInterfacePrbsState.h
struct CmdSetInterfacePrbsStateTraits : public BaseCommandTraits {
  static constexpr utils::ObjectArgTypeId ObjectArgTypeId =
      utils::ObjectArgTypeId::OBJECT_ARG_TYPE_PRBS_STATE;
  using ParentCmd = CmdSetInterfacePrbs;
  using ObjectArgType = utils::PrbsState; // used to be std::vector<std::string>
  using RetType = std::string;
};
```
Here describes what happens at run-time:

Each type of argument will be parsed as `std::vector<std::string>` from the command line interface. Since there are three arguments, the corresponding argument type of the whole command is `std::tuple<std::vector<std::string>, std::vector<std::string>, std::vector<std::string>>`

An implicit type conversion takes place in `fboss/cli/fboss2/CmdArgsLists.h::getTypedArgs`:

It will convert the `UnfilteredTypes`(`std::tuple<std::vector<std::string>, std::vector<std::string>, std::vector<std::string>>`) to `Types`(`std::tuple<utils::PortList, utils::PrbsComponent, utils::PrbsState>`)

During this conversion, the constructor of each type of argument is called. Validation will be applied automatically.

Therefore, in the file `CmdSetInterface.h` for command `fboss2 set interface [ports...]`, the second input parameter of `queryClient` is `utils::PortList ports`.

The second layer command `fboss2 set interface [ports...] prbs` inherts the argument from the upper layer, so the second and third parameters of `queryClient` in file `CmdSetInterfacePrbs.h` are `utils::PortList` and `utils::PrbsComponent`.

Accordingly, the input parameters for `fboss2 set interface [ports...] prbs [component...] state [states...]` become `utils::PortList`, `utils::PrbsComponent` and `utils::PrbsState`
